
package org.apli.haiam.controladores;

import com.apli.controladores.Aplinfo;
import com.apli.ejb.controladores.AbstractFacade;
import com.apli.ejb.exceptions.ApliException;
import com.apli.ejb.exceptions.TipoMensajeError;
import java.io.Serializable;
import java.lang.reflect.Method;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import org.apli.haiam.admin.entidades.UsuarioAdmin;
import org.apli.haiam.ejb.entidades.AdmAuditoria;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * clase Abstracta que implementa los métodos que administran los registros de
 * las tablas asociadas a las clases de entidad (EJB3). <p> Implementa en sus
 * meétodos la auditoría. 
 *
 * <H6>Soporte:HAIMA
 * <I>david_s_1086@hotmail.com,david.carranza@haiam.gob.ec</I></H6>
 * @author david Carranza
 * @version 1.0 30/07/2014
 * @param <T>
 */
public abstract class HaiamAbstractFacade<T> extends AbstractFacade<T> implements Serializable {

    /**
     * Definicón de constanetes para cada acción
     */
    private static final String CREAR = "Crear";
    private static final String CREAR_ACCION = "crear";
    private static final String MODIFICAR = "Modificar";
    private static final String MODIFICAR_ACCION = "modificar";
    private static final String ELIMINAR = "Eliminar";
    private static final String ELIMINAR_ACCION = "eliminar";
    /**
     * Variable para registrar sucesos del sistema
     */
    private static final Logger log = LoggerFactory.getLogger(HaiamAbstractFacade.class);

    /**
     * Crea un registro en la tabla asociada a la clase de entidad (EJB3).
     *
     * @param entity la entidad a crear
     * @param user el usuario que crea la entidad
     * @throws Exception si existe un error
     */
    @Override
    public void crear(T entity, Object user) throws Exception {
        String nombreMetodo = Thread.currentThread().getStackTrace()[1].getMethodName() + "(" + entity.getClass().getSimpleName() + "," + user.getClass().getSimpleName() + ")";
        UsuarioAdmin user1 = (UsuarioAdmin) user;
        try {
            validarUsuarioSistema(user1);
        } catch (Exception e) {
            log.error("{}: {}", nombreMetodo, e);
            throw e;
        }
        if (user1.getUser().getIdUsuario() != -1 && user1.getPermisosMap().get(CREAR_ACCION + entity.getClass().getSimpleName()) == null) {
            log.error("{}: No tiene permisos para la acción: {} un elemento de {} [PERMISO:{}]", new Object[]{nombreMetodo, CREAR_ACCION, entity.getClass().getSimpleName(), "crear" + entity.getClass().getSimpleName()});
            throw new ApliException(TipoMensajeError.USER_NO_AUTORIZADO);
        }
        /**
         * recupera método para nuevo código
         */
        Method metodo = null;
        Class c = getIdType(entity.getClass());
        if (c.equals(Integer.class) || c.equals(Short.class) || c.equals(Byte.class) || c.equals(Long.class)) {
            String campoId = getIdName(entity.getClass());
            String nomSetMetodo = "set" + campoId.substring(0, 1).toUpperCase() + campoId.substring(1);
            Method[] methods = entity.getClass().getDeclaredMethods();
            for (Method met : methods) {
                if (met.getName().equals(nomSetMetodo)) {
                    metodo = met;
                    break;
                }
            }
        }
        /**
         * FIN -- recupera método para nuevo código
         */
        try {
//            ctx.getUserTransaction().begin();
            // obtiene el nuevo código
            if (metodo != null) {
                metodo.invoke(entity, getNuevoCodigo(entity.getClass()));
            }
            /**
             * grabar Auditoría
             */
            Object logObj = Aplinfo.cloneObjectEjb(entity, false);
            String descripcion = "El usuario " + user1.getNombreFull()+ " creó un registro en " + entity.getClass().getName().substring(entity.getClass().getName().lastIndexOf(".") + 1);
            grabarAuditoria(CREAR, logObj, user1, descripcion);
            /**
             * FIN -- grabar Auditoría
             */
            getEntityManager().persist(entity);
//            ctx.getUserTransaction().commit();
        } catch (Exception e) {
            // pone el código a null
            if (metodo != null) {
                Object ob = null;
                metodo.invoke(entity, ob);
            }
//            try {
//                ctx.getUserTransaction().rollback();
//            } catch (Exception e1) {
//                log.error("crear(...) {}", e1.getMessage());
//            }
            log.error("{}: {}", nombreMetodo, e);
            if (e instanceof ApliException) {
                throw e;
            }
            throw new ApliException(TipoMensajeError.ERROR_GRABAR, e);
        }
    }

    /**
     * Modifica un registro en la tabla asociada a la clase de entidad (EJB3).
     *
     * @param entity la entidad a crear
     * @param user el usuario que modifica la entidad
     * @throws Exception si existe un error
     */
    @Override
    public void modificar(T entity, Object user) throws Exception {
        String nombreMetodo = Thread.currentThread().getStackTrace()[1].getMethodName() + "(" + entity.getClass().getSimpleName() + "," + user.getClass().getSimpleName() + ")";
        UsuarioAdmin user1 = (UsuarioAdmin) user;
        try {
            validarUsuarioSistema(user1);
        } catch (Exception e) {
            log.error("{}: {}", nombreMetodo, e);
            throw e;
        }
        if (user1.getUser().getIdUsuario() != -1 && user1.getPermisosMap().get(MODIFICAR_ACCION + entity.getClass().getSimpleName()) == null) {
            log.error("{}: No tiene permisos para la acción: {} un elemento de {} [PERMISO:{}]", new Object[]{nombreMetodo, MODIFICAR_ACCION, entity.getClass().getSimpleName(), "modificar" + entity.getClass().getSimpleName()});
            throw new ApliException(TipoMensajeError.USER_NO_AUTORIZADO);
        }
        try {
            /**
             * grabar Auditoría
             */
            Object logObj = Aplinfo.cloneObjectEjb(entity, false);
            String descripcion = "El usuario " + user1.getNombreFull() + " modificó un registro en " + entity.getClass().getName().substring(entity.getClass().getName().lastIndexOf(".") + 1);

            grabarAuditoria(MODIFICAR, logObj, user1, descripcion);
            /**
             * FIN -- grabar Auditoría
             */
            getEntityManager().merge(entity);
        } catch (Exception e) {
            log.error("{}: {}", nombreMetodo, e);
            throw new ApliException(TipoMensajeError.ERROR_MODIFICAR, e);
        }
    }

    /**
     * Eliminia un registro en la tabla asociada a la clase de entidad (EJB3).
     *
     * @param entity la entidad a crear
     * @param user el usuario que elimina la entidad
     * @throws Exception si existe un error
     */
    @Override
    public void eliminar(T entity, Object user) throws Exception {
        String nombreMetodo = Thread.currentThread().getStackTrace()[1].getMethodName() + "(" + entity.getClass().getSimpleName() + "," + user.getClass().getSimpleName() + ")";
        UsuarioAdmin user1 = (UsuarioAdmin) user;
        try {
            validarUsuarioSistema(user1);
        } catch (Exception e) {
            log.error("{}: {}", nombreMetodo, e);
            throw e;
        }
        if (user1.getUser().getIdUsuario() != -1 && user1.getPermisosMap().get(ELIMINAR_ACCION + entity.getClass().getSimpleName()) == null) {
            log.error("{}: No tiene permisos para la acción: {} un elemento de {} [PERMISO:{}]", new Object[]{nombreMetodo, ELIMINAR_ACCION, entity.getClass().getSimpleName(), "eliminar" + entity.getClass().getSimpleName()});
            throw new ApliException(TipoMensajeError.USER_NO_AUTORIZADO);
        }
        try {
            /**
             * grabar Auditoría
             */
            Object logObj = Aplinfo.cloneObjectEjb(entity, false);
            String descripcion = "El usuario " + user1.getNombreFull() + " eliminó un registro en " + entity.getClass().getName().substring(entity.getClass().getName().lastIndexOf(".") + 1);

            grabarAuditoria(ELIMINAR, logObj, user1, descripcion);
            /**
             * FIN -- grabar Auditoría
             */
            getEntityManager().remove(getEntityManager().merge(entity));
        } catch (Exception e) {
            log.error("{}: {}", nombreMetodo, e);
            throw new ApliException(TipoMensajeError.ERROR_ELIMINAR, e);
        }
    }

    /**
     * Verifica si el usuario es válido y tiene asociado a un sistema.
     *
     * @param user1 usuario de tipo MdmqUsuario
     * @throws Exception si existe un error
     */
    protected void validarUsuarioSistema(Object user1) throws Exception {
        String nombreMetodo = Thread.currentThread().getStackTrace()[1].getMethodName() + "(" + user1.getClass().getSimpleName() + ")";
        if (user1 == null) {
            log.error("{}: USUARIO NULL", nombreMetodo);
            throw new ApliException(TipoMensajeError.USER_NO_LOGIN);
        }
//        if (user1.getSistema() == null) {
//            log.info("eliminar(...) El sistema es null");
//            throw new ApliException(TipoMensajeError.SYSTEM_NULL);
//        }
    }

    /**
     * Graba la auditoría en la BD.
     *
     * @param nombreAccion
     * @param logObj el objeto a persistir en formato XML
     * @param user
     * @param descripcion la descripción corta para la auditoría
     * @throws Exception si existe un error
     */
    protected synchronized void grabarAuditoria(String nombreAccion, Object logObj, Object user, String descripcion) throws Exception {
        String nombreMetodo = Thread.currentThread().getStackTrace()[1].getMethodName() + "(" + nombreAccion + "," + logObj.getClass().getSimpleName() + "," + user.getClass().getSimpleName() + "," + descripcion + ")";
        UsuarioAdmin user1 = (UsuarioAdmin) user;
        try {
            AdmAuditoria dmpLog = new AdmAuditoria(Aplinfo.generarUUID());
           // dmpLog.setXmlData(Aplinfo.comprimirGz(Aplinfo.getStringXMLFromObject(logObj).getBytes()));
            dmpLog.setXmlData(Aplinfo.getStringXMLFromObject(logObj));
            dmpLog.setNombreUsuario(user1.getNombreFull());
            dmpLog.setTabla(logObj.getClass().getName().substring(logObj.getClass().getName().lastIndexOf(".") + 1));
            dmpLog.setFecha(getFechaDb());
            dmpLog.setAdmUsuario(user1.getUser());
            dmpLog.setIp(user1.getIp());
            dmpLog.setHost(user1.getHost());
            dmpLog.setNombreAccion(nombreAccion);

            dmpLog.setAdmSistema(user1.getSistema());
            dmpLog.setDescripcion(descripcion);

            getEntityManager().persist(dmpLog);
        } catch (Exception e) {
            log.error("{}: {}", nombreMetodo, e);
            throw e;
        }
    }

    public boolean grabarExcepcion(Object descripcion, Object stackTrace, String clase, String metodo, String tipo) {
        String nombreMetodo = Thread.currentThread().getStackTrace()[1].getMethodName() + "(" + descripcion + "," + stackTrace + "," + clase + "," + metodo + "," + tipo + ")";
        log.debug("{}: Grabando Excepción", nombreMetodo);
        try {
            Connection con = getEntityManager().unwrap(Connection.class);
//            con.setAutoCommit(true);
            String sql = "insert into ADM_EXCEPCION (ID_EXCEPCION, DESCRIPCION, STACK_TRACE, CLASE, METODO, TIPO) values (?,?,?,?,?,?)";
            PreparedStatement pstm = con.prepareStatement(sql);
            pstm.setString(1, Aplinfo.generarUUID());
            pstm.setString(2, descripcion.toString());
            pstm.setString(3, stackTrace.toString());
            pstm.setString(4, clase);
            pstm.setString(5, metodo);
            pstm.setString(6, tipo);
            return pstm.executeUpdate() > 0;
        } catch (SQLException e) {
            log.error("{}: {}", e.getMessage());
            return false;
        }
    }
   
    
}
